﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using TvTipper.Api;
using TvTipper.Entities;
using NHibernate.Cfg;
using NHibernate;

namespace TvTipper.Core
{
    public class NHibernateDataAccessPortal : IDataAccessPortal
    {
        private Configuration m_cfg;
        private ISessionFactory m_sessionFactory;

        public NHibernateDataAccessPortal()
        {
            m_cfg = new Configuration().Configure();
            m_sessionFactory = m_cfg.BuildSessionFactory();
        }

        public IList<PersonMetadata> SearchPersonsByName(string name)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("SearchPerson");
                q.SetString("person_name", name);
                return q.List<PersonMetadata>();
            }
        }

        public Person GetPersonById(string id)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("GetPersonInfo");
                q.SetString("person_id", id);
                var person = q.List<Person>().FirstOrDefault();
                if (person != null)
                {
                    q = session.GetNamedQuery("GetPersonRoles");
                    q.SetString("person_id", id);
                    person.Roles = q.List<RoleEntity>();
                }


                return person;
            }
        }

        public IList<TvProgramMetadata> SearchTvProgram(TvProgramSearchQuery query)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var strYear = string.IsNullOrEmpty(query.Year) ? null : query.Year;
                var q = session.GetNamedQuery("SearchProgram");
                q.SetString("program_name", query.Name);
                q.SetString("program_year", strYear);
                return q.List<TvProgramMetadata>();
            }
        }

        public TvProgram GetTvProgramById(string id)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("GetProgramInfo");
                q.SetString("prog_id", id);
                var program = q.List<TvProgram>().FirstOrDefault();
                if (program != null)
                {
                    // case
                    q = session.GetNamedQuery("GetProgramCast");
                    q.SetString("prog_id", id);
                    program.Cast = q.List<CastEntity>();

                    // generes
                    q = session.GetNamedQuery("GetProgramGeneres");
                    q.SetString("prog_id", id);
                    program.Generes = q.List<int>();
                }

                return program;
            }
        }

        public IList<TvProgramMetadata> GetTopTvPrograms(int from, int to)
        {
            try
            {
                using (var session = m_sessionFactory.OpenSession())
                {
                    var q = session.GetNamedQuery("GetTopPrograms");
                    q.SetInt32("start", from);
                    q.SetInt32("end", to);
                    return q.List<TvProgramMetadata>();
                }
            }
            finally
            {
                Services.EntityManager.RecommandationUpdated = true;
            }
        }

        public IList<LookupEntity> getLookupByKey(LookupType type)
        {
            string qName = "";

            switch (type)
            {
                case LookupType.Role:
                    qName = "GetRoles";
                    break;
                case LookupType.Genere:
                    qName = "GetGeneres";
                    break;
                default:
                    throw new ArgumentException("Unsupported lookup type");
            }

            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery(qName);
                return q.List<LookupEntity>();
            }
        }


        private void RankPerson(string id, int rank)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("RankPerson");
                q.SetString("person_id", id);
                q.SetInt32("new_rank", rank);
                q.ExecuteUpdate();
            }
        }

        private void RankTvProgram(string id, int rank)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("RankProgram");
                q.SetString("program_id", id);
                q.SetInt32("new_rank", rank);
                q.ExecuteUpdate();
            }
        }

        public void RankEntity(RankableEntity entity, int rank)
        {
            if (entity is PersonMetadata)
            {
                RankPerson(entity.Id, rank);
            }
            else if (entity is TvProgramMetadata)
            {
                RankTvProgram(entity.Id, rank);
            }
            else
            {
                throw new ArgumentException("Invalid entity type");
            }
        }

        public IDictionary<string, float> GetRankFactors()
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("GetFactors");
                var list = q.List();

                var th = new Dictionary<string, float>();
                foreach (var p in list)
                {
                    var array = (Object[])p;
                    th.Add(array[0].ToString(), float.Parse(array[1].ToString()));
                }

                return th;
            }
        }


        public void SetRankFactors(IDictionary<string, float> factors)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("UpdateFactors");
                q.SetDouble("actor", factors["actor"]);
                q.SetDouble("creator", factors["creator"]);
                q.SetDouble("genre", factors["genre"]);
                q.ExecuteUpdate();
            }
        }

        public void UpdatePerson(Person person)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("UpdatePerson");
                q.SetString("person_id", person.Id);
                q.SetString("person_name", person.Name);
                q.SetString("person_info", person.Info);
                q.ExecuteUpdate();
            }
        }

        public void UpdateProgram(TvProgram program)
        {
            using (var session = m_sessionFactory.OpenSession())
            {
                var q = session.GetNamedQuery("UpdateProgram");
                q.SetString("id", program.Id);
                q.SetString("name", program.Name);
                q.SetString("desc", program.Description);
                q.SetString("start_year", program.StartYear.HasValue ? program.StartYear.ToString() : "");
                q.SetString("end_year", program.EndYear.HasValue ? program.EndYear.ToString() : "");
                q.SetParameter("seasons", program.Seasons, NHibernateUtil.Int32);
                q.ExecuteUpdate();
            }
        }
    }
}
